/************************************************************************

								Heroes IV
					  Copyright 2001, The 3DO Company

	------------------------------------------------------------------
  					        school_of_magic.cpp

	$Header: /game/school_of_magic.cpp $

	$NoKeywords: $

 ************************************************************************/
// kill the 255+ character identifier warning.  Templates tend to exceed that easily.
#include "precompiled.h"
#pragma warning( disable: 4786 )

#include "school_of_magic.h"

#include "adventure_frame.h"
#include "adventure_map.h"
#include "adventure_sounds.h"
#include "ai_value.h"
#include "army.h"
#include "bitmap_group.h"
#include "bitmap_group_cache.h"
#include "bitmap_layer_window.h"
#include "creature_stack.h"
#include "dialog_school_of_magic.h"
#include "external_string.h"
#include "hero.h"
#include "hero_keyword_replacer.h"
#include "mouse_event.h"
#include "music.h"
#include "object_registration.h"
#include "player.h"
#include "random.h"
#include "select_skill_from_set.h"
#include "simple_dialog.h"
#include "skill.h"
#include "skill_properties.h"
#include "skill_set.h"

extern t_external_string const k_text_dead_heroes_ineligible;

namespace
{
	int const k_current_map_format_version = 1;
	
	/*
	** Both School of Magic and School of War are in this file, since
	** their functionality is similar.
	*/

	t_object_registration<t_school_of_magic> k_registration( k_adv_object_school, 
															 k_school_of_magic );

	t_object_registration<t_school_of_war> k_school_of_war_registration( k_adv_object_school, 
																	 	 k_school_of_war );

	const int k_skill_cost    = 2000;
}

/*-------------------------------------------------------------*\
** Selected hero can learn magic from school of magic.
\*-------------------------------------------------------------*/
t_school_of_magic::t_school_of_magic( std::string const& source )
         : t_single_use_object( source ),
		   m_available_skills ( ~t_skill_set() ),
		   m_skill_1( k_skill_none ),
		   m_skill_2( k_skill_none )
{

}


void t_school_of_magic::activate_trigger( t_army* army, t_adv_map_point const& point, 
		                                    t_direction direction, t_adventure_frame* frame )
{
	int                     i;
	t_hero*                 hero;
	std::vector<t_hero*>    heroes;
	bool				    any_heroes_eligible = false;
	bool                    dead_heroes = true;
	t_creature_array&       creatures = army->get_creatures();
	std::string             text;
    int                     random_skill_index = 0;

	if (army->get_owner_number() >= 0)
		m_visited[army->get_owner_number()] = true;
	else 
		return;

	// AI decider
	if (army->get_owner()->is_computer()) 
	{
		for (int i = 0; i < t_creature_array::k_size; i++) 
		{
			t_hero* hero = (*army)[i].get_hero();
			if (!hero) continue;
			
			if (!player_can_afford( army, k_skill_cost ))  // Stop if we've run out of cash
				break;

			float skill_1 = ai_value_of_skill_increase_minus_cost( hero, t_skill( m_skill_1, k_mastery_basic ), k_skill_cost, army );
			float skill_2 = ai_value_of_skill_increase_minus_cost( hero, t_skill( m_skill_2, k_mastery_basic ), k_skill_cost, army );
			t_skill_type type;
			if (skill_1 > skill_2) 
			{
				if (skill_1 <= 0.0f) 
					continue;
				type = m_skill_1;
			} else {
				if (skill_2 <= 0.0f)
					continue;
				type = m_skill_2;
			}

			hero->learn_skill(t_skill(type, k_mastery_basic));
			hero->update_class();
			army->get_owner()->spend( k_gold, k_skill_cost );
			hero->set_visited( *this );
		}
		return;
	}

    bool    any_heroes = false;
    /*
    ** Check for eligible heroes.
    */
	for (i = 0; i < t_creature_array::k_size; i++)
	{
		hero = creatures[i].get_hero();

        // Hero or not?        
		if (hero == 0)
			continue;
	    
        any_heroes = true;

		if (hero->is_dead())
			continue;
	    dead_heroes = false;

		heroes.push_back( hero );

        if (hero->get_primary_skill_count() >= k_primary_skill_limit)
            continue;

        // If hero knows both skills.
        if ((hero->get_skill(m_skill_1) > k_mastery_none) && (hero->get_skill(m_skill_2) > k_mastery_none))
            continue;
        
        /*
        ** Has this hero visited?  This check
        ** may not be necessary.
        */
		if (hero->has_visited( *this ))
			continue;
		any_heroes_eligible = true;

	}
    
	t_sound_cache music = get_music_playing();
	stop_music();
	get_dialog_sound( k_dialog_sound_university )->play( get_sound_volume() );

    /*
    ** If heroes aren't eligible for upgrade, display appropiate dialog.
    */
    std::string status_text;
    // No heroes.
    if (any_heroes == false)
    {
        status_text = get_text ("Empty");
	    ok_dialog( status_text, get_dialog_bottom( point ) );
		play_music( music );
        return;
    }
    if (dead_heroes == true)
    {
        status_text = get_text("heroes_dead");
        if (status_text == "")
            status_text = k_text_dead_heroes_ineligible;
        ok_dialog( status_text, get_dialog_bottom( point ) );
		play_music( music );
        return;
    }
    if (any_heroes_eligible == false)
    {
        
        // There are heroes, but they've all visited.
		if (heroes.size() > 0)
        {
            status_text = get_text ("Empty");
	        ok_dialog( status_text, get_dialog_bottom( point ) );
        }
        // No heroes, show app. message...
        else
        {
            status_text = get_text ("Empty");
	        ok_dialog( status_text, get_dialog_bottom( point ) );
        }
		play_music( music );
        return;
    }

	select_heroes_dialog ( heroes, frame, army);
	play_music( music );
}

/*--------------------------------------------------------------------*\
** Default available skills for old map files
\*--------------------------------------------------------------------*/
t_skill_set const & t_school_of_magic::get_default_available_skill_set()
{
	return ::get_skill_set(true, false, true, false);
}

/*--------------------------------------------------------------------*\
** Return version.
\*--------------------------------------------------------------------*/
int t_school_of_magic::get_version() const
{
	return 2;
}

// ------------------------------------------------------------------------
// Read in primary magic skills for save game.
// ------------------------------------------------------------------------
bool t_school_of_magic::read( std::streambuf&						stream, 
						      t_qualified_adv_object_type const&	type,
						      int									version )
{
	if (version < 1)
	{
		// Choose randomly in place()
		m_available_skills = get_default_available_skill_set();
		m_required_skills.reset();

		return true;
	}

	if (version == 1)
	{
		t_single_use_object::read( stream, type, 0 );
	}
	else
	{
		int single_use_version = get<int>( stream );
		
		if (!t_single_use_object::read( stream, type, single_use_version ))
			return false;
	}

	m_skill_1 = t_skill_type( get<t_uint8>( stream ));
	m_skill_2 = t_skill_type( get<t_uint8>( stream ));

	return m_visited.read( stream );
}

// ------------------------------------------------------------------------
// Read from new map
// ------------------------------------------------------------------------
bool t_school_of_magic::read_from_map( std::streambuf& stream, t_progress_handler* progress)
{
	int version = get< t_uint16 >( stream );
	if ( version < 0 || version > k_current_map_format_version )
		return false;

	if (version == 0)
	{
		m_available_skills = get_default_available_skill_set();
		m_required_skills.reset();
	}
	else
	{
		if (!m_available_skills.read( stream ) ||
			!m_required_skills.read( stream ))
		{
			return false;
		}
	}

	return true;
}

void t_school_of_magic::place( t_adventure_map& map, t_adv_map_point const & point)
{
	t_single_use_object::place( map, point );

	if ( m_skill_1 == k_skill_none )
	{
		// Choose skills. We can't do this in read_from_map() since we don't have
		// the map's list of allowed skills yet
		t_skill_set available_skills = m_available_skills & map.get_allowed_skills();

		if ( available_skills.none() )
		{
			available_skills = m_available_skills;
			if ( available_skills.none() )
			{
				available_skills = map.get_allowed_skills();
				if ( available_skills.none() )
				{
					available_skills = get_default_available_skill_set();
				}
			}
		}

		m_skill_1 = select_skill_from_set(available_skills, m_required_skills);

		available_skills[m_skill_1] = false;
		m_required_skills[m_skill_1] = false;

		if ( available_skills.none() )
		{
			available_skills = m_available_skills;
			available_skills[m_skill_1] = false;

			if ( available_skills.none() )
			{
				available_skills = map.get_allowed_skills();
				available_skills[m_skill_1] = false;
		
				if ( available_skills.none() )
				{
					available_skills = get_default_available_skill_set();
					available_skills[m_skill_1] = false;
				}
			}
		
		}

		m_skill_2 = select_skill_from_set(available_skills, m_required_skills);
	}
}

// ------------------------------------------------------------------------
// Write out primary magic skills for save game.
// ------------------------------------------------------------------------
bool t_school_of_magic::write( std::streambuf& stream ) const
{
	put<int>( stream, t_single_use_object::get_version() );

    if (!t_single_use_object::write(stream))
		return false;

	put<t_uint8>( stream, m_skill_1 );
	put<t_uint8>( stream, m_skill_2 );

	return m_visited.write( stream );
}

// -----------------------------------------------------------------------------------------
// Skill & hero select dialog - put here so we can change text names if necessary.
// -----------------------------------------------------------------------------------------
void t_school_of_magic::select_heroes_dialog( std::vector<t_hero*> const& heroes, t_window* parent, t_army* army )
{
	t_counted_ptr<t_dialog_school_of_magic> dialog_ptr;

	dialog_ptr = new t_dialog_school_of_magic( parent );
	dialog_ptr->init_dialog( heroes, parent, *this, army, m_skill_1, m_skill_2);
	dialog_ptr->run_modal();
}

/*-----------------------------------------------------------------------------*\
** Selected hero can buy primary skill from visiting a school of magic.
\*-----------------------------------------------------------------------------*/
void t_school_of_magic::visit( t_hero* hero )
{

}

float t_school_of_magic::sum_available_skill_values( t_creature_array const& army ) const
{
	float value = 0.0f;
	for (int i = 0; i < t_creature_array::k_size; i++) 
	{
		t_hero const* hero = army[i].get_hero();
		if (!hero) continue;

		float skill_1 = ai_value_of_skill_increase_minus_cost( hero, t_skill( m_skill_1, k_mastery_basic ), k_skill_cost, &army );
		float skill_2 = ai_value_of_skill_increase_minus_cost( hero, t_skill( m_skill_2, k_mastery_basic ), k_skill_cost, &army );
		float skill_value = (skill_1 > skill_2) ? skill_1 : skill_2;
		if (skill_value > 0.0f) 
			value += skill_value;
	}
	return value;
}

float t_school_of_magic::ai_value( t_adventure_ai const& ai, t_creature_array const& army, int move_cost ) const
{
	if (!player_can_afford( &army, k_skill_cost ))
		return 0.0f;

	if (m_visited[get_player_number( army )])
		return sum_available_skill_values( army );
	else 
		return ai_summed_primary_skill_value( k_skill_cost, false, &army, this );
}

void t_school_of_magic::right_click( t_mouse_event const& event,
	                                 t_adventure_frame* adventure_frame )
{
	t_skill_mastery information = get_information_level();
	int				player_number = get_map()->get_player_number();

	if (information >= k_mastery_basic)
		m_visited[player_number] = true;
	if (!m_visited[player_number])
	{
		t_single_use_object::right_click( event, adventure_frame );
		return;
	}
	std::string				help_text = get_text( "help_skills" );
	t_hero_keyword_replacer replacer;

	replacer.add_skill( t_skill( m_skill_1, k_mastery_basic ) );
	replacer.add_skill( t_skill( m_skill_2, k_mastery_basic ) );

	show_popup_text( replacer( help_text ), event.screen_point );
}

/*-----------------------------------------------------------------------------*\
**
** School of war.
**
\*-----------------------------------------------------------------------------*/
t_school_of_war::t_school_of_war( std::string const& source )
               : t_school_of_magic (source)
{
}


/*--------------------------------------------------------------------*\
** Default available skills for old map files
\*--------------------------------------------------------------------*/
t_skill_set const & t_school_of_war::get_default_available_skill_set()
{
	return ::get_skill_set(true, false, false, true);
}

float t_school_of_war::ai_value( t_adventure_ai const& ai, t_creature_array const& army, int move_cost ) const
{
	if (!player_can_afford( &army, k_skill_cost ))
		return 0.0f;

	if (m_visited[get_player_number(army)])
		return sum_available_skill_values( army );
	else 
		return ai_summed_primary_skill_value( k_skill_cost, true, &army, this );
}


